home *** CD-ROM | disk | FTP | other *** search
/ Stone Design / Stone Design.iso / Stone_Friends / NeXT-Icons / next-icon@gun.com / Apps / ImagePortfolio / ImagePortfolio.cxx < prev    next >
Encoding:
C/C++ Source or Header  |  1993-06-03  |  15.3 KB  |  526 lines

  1. // -------------------------------------------------------------------------------------
  2. // ImagePortfolio.m - Visual TIFF/EPS file manager
  3. // -------------------------------------------------------------------------------------
  4. // Permission is granted to freely redistribute this source code, and to use fragments
  5. // of this code in your own applications if you find them to be useful.  This class,
  6. // along with the source code, come with no warranty of any kind, and the user assumes
  7. // all responsibility for its use.
  8. // -------------------------------------------------------------------------------------
  9.  
  10. extern "Objective-C" {
  11. #import <objc/objc.h>
  12. #import <appkit/appkit.h>
  13. #import <libc.h>
  14. #import <stdlib.h>
  15. #import <stdio.h>
  16. #import <string.h>
  17. #import <mach/mach.h>
  18. #import <defaults/defaults.h>
  19. #import <dpsclient/wraps.h>
  20. #import <3Dkit/N3DRIBImageRep.h>
  21. #import "appUtils.h"
  22. #import "fileUtils.h"
  23. #import "ScrollText.h"
  24. }
  25.  
  26. #import "GifImageRep.h"
  27. #import "ImagePortfolio.h"
  28. #import "Portfolio.h"
  29. #import "PaletteCell.h"        // for adding support image types
  30. #import "PaletteMatrix.h"
  31.  
  32. // -------------------------------------------------------------------------------------
  33. // application version string
  34. extern "C" { extern char *appVersion; }
  35.  
  36. // -------------------------------------------------------------------------------------
  37. // HELP file name (in language directory)
  38. #define HELP_FILE            "HELP.rtfd"
  39.  
  40. // -------------------------------------------------------------------------------------
  41. // preferences
  42. static BOOL                    prefUseNXImageClass = YES;
  43.  
  44. // -------------------------------------------------------------------------------------
  45. // NXImages
  46. static id                    dockIcon = (id)nil;
  47. static id                    openFolder = (id)nil;
  48.  
  49. // -------------------------------------------------------------------------------------
  50. // PasteBoard dragging operation variables
  51. // Note: Only one drag operation can occur at any given time.
  52. BOOL                        pbDragFilesOk = NO;
  53. NXAtom                        pbTypes[] = { NXFilenamePboardType };
  54. int                            pbNumTypes = 1;
  55. int                            pbChangeCount = 0;
  56.  
  57. // -------------------------------------------------------------------------------------
  58. // This is done for the purpose of allowing to setDelegate on the appIcon window
  59. // [DelegateWindow poseAs:[Window class]] must be issued in +new(Application) 
  60. @interface DelegateWindow:Window @end
  61. @implementation DelegateWindow
  62. - setDelegate:sender
  63. {
  64.     if ((wFlags.style != NX_TOKENSTYLE) && (wFlags.style != NX_MINIWORLDSTYLE)) {
  65.         [super setDelegate:sender];
  66.     } else {
  67.         int saveStyle = wFlags.style;
  68.         wFlags.style = NX_PLAINSTYLE;
  69.         [super setDelegate:sender];
  70.         wFlags.style = saveStyle;
  71.     }
  72.     return self;
  73. }
  74. @end
  75.  
  76. // -------------------------------------------------------------------------------------
  77. @implementation ImagePortfolio
  78.  
  79. // -------------------------------------------------------------------------------------
  80. // menu/button actions
  81.  
  82. /* make and show new image Palette */
  83. - newImagePalette:sender
  84. {
  85.     return [[Portfolio alloc] initFromList:(ParseString*)nil];
  86. }
  87.  
  88. /* open Palette file */
  89. - open:sender
  90. {
  91.     id            pOpen;
  92.     char        fName[MAXPATHLEN+1], *dir, **files, *fTypes[] = { docEXTENSION, 0 };
  93.  
  94.     /* build/display open panel */
  95.     pOpen = [OpenPanel new];
  96.     [pOpen setTitle:"Open Image Portfolio"];
  97.     [pOpen setPrompt:"File:"];
  98.     [pOpen setRequiredFileType:fTypes[0]];
  99.     [pOpen allowMultipleFiles:YES];
  100.     [pOpen setDirectory:[NXApp lastPath]];
  101.     if (![pOpen runModalForTypes:fTypes]) return self;
  102.  
  103.     /* open the list of selected files */
  104.     if (!(files = (char**)[pOpen filenames])) return self;
  105.     if (dir=(char*)[pOpen directory]) { chdir(dir); [self setLastPath:dir]; }
  106.     while (*files) {
  107.         sprintf(fName, "%s/%s", dir, *files++);
  108.         ParseString fileNames = fName;
  109.         [[Portfolio alloc] initFromList:&fileNames];
  110.     }
  111.  
  112.     return self;
  113. }
  114.  
  115. /* arrange windows in front */
  116. - arrangeInFront:sender
  117. {
  118.     return self;
  119. }
  120.  
  121. /* show info panel */
  122. - showInfo:sender
  123. {
  124.     if (!infoIsLoaded) {
  125.         [infoPanel center];
  126.         infoIsLoaded = YES;
  127.     }
  128.     return [infoPanel makeKeyAndOrderFront:sender];
  129. }
  130.  
  131. /* show help panel */
  132. - showHelp:sender
  133. {
  134.     if (!helpIsLoaded) {
  135.         char path[MAXPATHLEN + 1];
  136.         NXBundle *mBundle = [NXBundle mainBundle];
  137.         BOOL found = [mBundle getPath:path forResource:HELP_FILE ofType:(char*)nil];
  138.         if (!found) { NXBeep(); return self; }
  139.         [[helpScroll docView] openRTFDFrom:path];
  140.         [helpPanel center];
  141.         helpIsLoaded = YES;
  142.     }
  143.     return [helpPanel makeKeyAndOrderFront:sender];
  144. }
  145.  
  146. /* show preferences panel (to be called by PaletteMatrix only!) */
  147. - showPreferences:sender
  148. {
  149.     id            winDel = [Portfolio activePortfolio];
  150.     int            rows, cols;
  151.   
  152.     /* exit if not proper portfolio type (must match sender) */
  153.     if (!winDel || ![winDel isKindOf:[Portfolio class]]) return self;
  154.     
  155.     /* set screen fields */
  156.     [fontName setStringValue:[[winDel font] name]];
  157.     [fontSize setFloatValue:[[winDel font] pointSize]];
  158.     [prefCellWidth  setFloatValue:[winDel cellSize]->width ];
  159.     [prefCellHeight setFloatValue:[winDel cellSize]->height];
  160.     [winDel getDisplayedRows:&rows cols:&cols];
  161.     [prefCellRows setIntValue:rows];
  162.     [prefCellColumns setIntValue:cols];
  163.  
  164.     /* pref panel init */
  165.     if (!prefIsLoaded) {
  166.         [prefPanel center];
  167.         prefIsLoaded = YES;
  168.     }
  169.   
  170.     /* show window */
  171.     [prefPanel makeKeyAndOrderFront:sender];
  172.     [NXApp runModalFor:prefPanel];
  173.   
  174.     return self;
  175. }
  176.  
  177. /* cancel preferences */
  178. - cancelPreferences:sender
  179. {
  180.     [NXApp stopModal:0];
  181.     [prefPanel orderOut:sender];
  182.     return self;
  183. }
  184.  
  185. /* save preference to defaults table */
  186. - saveDefaults:sender
  187. {
  188.     id    winDel = [Portfolio activePortfolio];
  189.     if (winDel && [winDel isKindOf:[Portfolio class]]) [winDel saveDefaults:sender];
  190.     return self;
  191. }
  192.  
  193. /* print key window */
  194. - printKeyWindow:sender
  195. {
  196.     return (keyWindow)? [keyWindow smartPrintPSCode:sender] : (id)nil;
  197. }
  198.  
  199. // -------------------------------------------------------------------------------------
  200. // return application constants
  201.  
  202. /* set last default saved/openned file path */
  203. - setLastPath:(char*)path
  204. {
  205.     if (lastPath) free(lastPath);
  206.     lastPath = NXCopyStringBuffer(path);
  207.     return self;
  208. }
  209.  
  210. /* return last saved/openned path */
  211. - (const char*)lastPath
  212. {
  213.     return (lastPath)? lastPath : NXHomeDirectory();
  214. }
  215.  
  216. // -------------------------------------------------------------------------------------
  217. // inline event dispatcher
  218. // - When this method is interleaved with other methods, such as loading a large image
  219. //   directory, this allows continued interaction with the user.
  220. - dispatchEvents
  221. {
  222.     NXEvent    *e;
  223.     while (e=[NXApp getNextEvent:NX_MOUSEDOWNMASK waitFor:0.0 threshold:NX_BASETHRESHOLD])
  224.         [NXApp sendEvent:e];
  225.     return self;
  226. }
  227.   
  228. // -------------------------------------------------------------------------------------
  229. // menu cell update handler methods
  230.  
  231. - _setMenuCellsUpdateAction:menuId
  232. {
  233.     id    cellList = [[menuId itemList] cellList];
  234.     int    i = [cellList count];
  235.     while(i) {
  236.         id cell = [cellList objectAt:--i];
  237.         SEL    updAct, cSel = [cell action];
  238.         if ([cell hasSubmenu]) { [self _setMenuCellsUpdateAction:[cell target]]; continue; }
  239.         if ([cell target]) continue;
  240.         updAct = @selector(_ucKeyWindow:);    // default updateAction
  241.         if (cSel==@selector(cut:)               ) updAct=@selector(_ucCutCopyDelete:); else
  242.         if (cSel==@selector(copy:)                ) updAct=@selector(_ucCutCopyDelete:); else
  243.         if (cSel==@selector(delete:)            ) updAct=@selector(_ucCutCopyDelete:); else
  244.         if (cSel==@selector(undelete:)            ) updAct=@selector(_ucUndelete:);      else
  245.         if (cSel==@selector(paste:)                ) updAct=@selector(_ucResponder:);     else
  246.         if (cSel==@selector(save:)                ) updAct=@selector(_ucSelectable:);    else
  247.         if (cSel==@selector(saveAs:)            ) updAct=@selector(_ucSelectable:);    else
  248.         if (cSel==@selector(print:)                ) updAct=@selector(_ucSelectable:);    else
  249.         if (cSel==@selector(selectAll:)            ) updAct=@selector(_ucSelectable:);    else
  250.         if (cSel==@selector(sortByCellTitle:)   ) updAct=@selector(_ucSelectable:);    else
  251.         if (cSel==@selector(showPreferences:)   ) updAct=@selector(_ucMainWindow:);    else
  252.         if (cSel==@selector(performClose:)        ) updAct=@selector(_ucKeyWindow:);     else
  253.         if (cSel==@selector(performMiniaturize:)) updAct=@selector(_ucKeyWindow:);     else
  254.         if (cSel==@selector(printKeyWindow:)    ) updAct=@selector(_ucKeyWindow:);
  255.         [cell setUpdateAction:updAct forMenu:menuId];
  256.         [menuId setAutoupdate:YES];
  257.     }
  258.     return self;
  259. }
  260.  
  261. /* return menuCell responder */
  262. - _cellResponder:cellId
  263. {
  264.     id    respId = (keyWindow)? [keyWindow firstResponder] : (id)nil;
  265.     return (respId && [respId respondsTo:[cellId action]])? respId : (id)nil;
  266. }
  267.  
  268. /* return menuCell enabling flag */
  269. - (BOOL)_cellEnabler:cellId :(SEL)meth
  270. {
  271.     id    respId = [self _cellResponder:cellId];
  272.     [cellId setEnabled:((respId&&[respId respondsTo:meth]&&[respId perform:meth])?YES:NO)];
  273.     return YES;
  274. }
  275.  
  276. /* print/save/saveAs/selectAll: cells selectable */
  277. - (BOOL)_ucSelectable:cellId
  278. {
  279.     return [self _cellEnabler:cellId :@selector(anySelectableCells)];
  280. }
  281.  
  282. /* cut/copy/delete */
  283. - (BOOL)_ucCutCopyDelete:cellId
  284. {
  285.     return [self _cellEnabler:cellId :@selector(selectedCell)];
  286. }
  287.  
  288. /* undelete */
  289. - (BOOL)_ucUndelete:cellId
  290. {
  291.     return [self _cellEnabler:cellId :@selector(undeletable)];
  292. }
  293.  
  294. /* keyWindow menu cell update method */
  295. - (BOOL)_ucKeyWindow:cellId
  296. {
  297.     [cellId setEnabled:(keyWindow?YES:NO)];
  298.     return YES;
  299. }
  300.  
  301. /* mainWindow menu cell update method */
  302. - (BOOL)_ucMainWindow:cellId
  303. {
  304.     [cellId setEnabled:(mainWindow?YES:NO)];
  305.     return YES;
  306. }
  307.  
  308. /* paste */
  309. - (BOOL)_ucResponder:cellId
  310. {
  311.     [cellId setEnabled:([self _cellResponder:cellId]?YES:NO)];
  312.     return YES;
  313. }
  314.  
  315. // -------------------------------------------------------------------------------------
  316. // application delegate
  317.  
  318. /* Application instantiation */
  319. + new
  320. {
  321.     [DelegateWindow poseAs:[Window class]];
  322.     return [super new];
  323. }
  324.  
  325. /* application initialization */
  326. - appDidInit:sender
  327. {
  328.     int        globalWinNum;
  329.  
  330.     /* clear vars */
  331.     lastPath = (char*)nil;
  332.     helpIsLoaded = NO;
  333.     infoIsLoaded = NO;
  334.     prefIsLoaded = NO;
  335.     
  336.     /* cache app path */
  337.     XAppPath();
  338.     
  339.     /* load constant NXImages */
  340.     if (!dockIcon)   dockIcon   = [NXImage findImageNamed:"app"];
  341.     if (!openFolder) openFolder = [NXImage findImageNamed:"openFolder"];
  342.     
  343.     /* set the first responder update actions */
  344.     [self setAutoupdate:YES];
  345.     [self _setMenuCellsUpdateAction:[NXApp mainMenu]];
  346.  
  347.     /* set version number */
  348.     [versionNum setStringValue:appVersion];
  349.  
  350.     /* add supported image file types */
  351.     // these file extensions are checked for quick image qualification since many non-image
  352.     // files may pass through a Portfolio window.  A preference should be able to be set
  353.     // that allows using NXImage as a catch-all when these fail. 'prefUseNXImageClass' is
  354.     // currently hardcoded in this module.
  355.     [PaletteCell addImageClass:[GifImageRep class]];
  356.     [PaletteCell addImageExtensions:".gif", (char*)nil];
  357.     if (prefUseNXImageClass) {
  358.         [PaletteCell addImageClass:[NXImage class]];
  359.         [PaletteCell addImageExtensions:"*"];
  360.     } else {
  361.         [PaletteCell addImageClass:[NXBitmapImageRep class]];
  362.         [PaletteCell addImageClass:[NXEPSImageRep class]];
  363.         [PaletteCell addImageClass:[N3DRIBImageRep class]];
  364.         [PaletteCell addImageExtensions:".tiff",".tif",".icon",(char*)nil];
  365.         [PaletteCell addImageExtensions:".eps",".ps",".ai",(char*)nil];
  366.         [PaletteCell addImageExtensions:".rib",(char*)nil];
  367.     }
  368.  
  369.     /* make app active */
  370.     [NXApp activateSelf:YES];
  371.   
  372.     /* find real appIcon local window number and register for dragging */
  373.     [[NXApp appIcon] setDelegate:self];
  374.     NXConvertWinNumToGlobal([[NXApp appIcon] windowNum], (u_int*)&globalWinNum);
  375.     NXConvertGlobalToWinNum(globalWinNum, (u_int*)&appIconWinNum);
  376.     [[NXApp appIcon] registerForDraggedTypes:pbTypes count:pbNumTypes];
  377.  
  378.     return self;
  379. }
  380.  
  381. /* application termination (check windows for unsaved files) */
  382. - appWillTerminate:sender
  383. {
  384.     
  385.     /* display panel if unsaved files are found */
  386.     if ([Portfolio isDocEdited]) {
  387.         int rtn;
  388.         const char *msg, *quit, *qAny, *cncl;
  389.         msg  = NXLocalizedString("There are edited windows.", (char*)nil,(char*)nil);
  390.         quit = NXLocalizedString("Quit", (char*)nil,(char*)nil);
  391.         qAny = NXLocalizedString("Quit Anyway", (char*)nil,(char*)nil);
  392.         cncl = NXLocalizedString("Cancel", (char*)nil,(char*)nil);
  393.         rtn = NXRunAlertPanel(quit, msg, qAny, cncl, (char*)nil);
  394.         if (rtn == NX_ALERTALTERNATE) return (id)nil;
  395.     }
  396.     
  397.     /* MUST unregister appIcon window, otherwise drag may fail next time app is launched */
  398.     [[NXApp appIcon] unregisterDraggedTypes];
  399.     
  400.     /* return */
  401.     return self;
  402. }
  403.  
  404. // -------------------------------------------------------------------------------------
  405. // Command-Shift file dragging
  406. static Portfolio    *portfolioId = (id)nil;
  407.  
  408. /* delayed clear porfolioId */
  409. - _clearPortfolioId:sender
  410. {
  411.     portfolioId = (id)nil;
  412.     return self;
  413. }
  414.  
  415. /* open a Workspace Manager provided file */
  416. - (int)appOpenFile:(char*)filename type:(char*)type
  417. {
  418.     BOOL isDoc = strcmp(XFileExtension(filename), dotDocEXTENSION)? NO : YES;
  419.     ParseString imageFiles = filename;
  420.     
  421.     /* load file */
  422.     if (isDoc) [[Portfolio alloc] initFromList:&imageFiles];
  423.     else {
  424.         if (!portfolioId) portfolioId = [[Portfolio alloc] initFromList:&imageFiles];
  425.         else [portfolioId loadFileList:&imageFiles :NO :YES];
  426.     }
  427.     
  428.     /* restart portfolioId clear */
  429.     [self perform:@selector(_clearPortfolioId:) with:self
  430.         afterDelay:2000 cancelPrevious:YES];
  431.  
  432.     return YES;
  433. }
  434.  
  435. /* indicate to Workspace Manager that I accept files */
  436. - (BOOL)appAcceptsAnotherFile:sender { return YES; }
  437.  
  438. // -------------------------------------------------------------------------------------
  439. // application icon window delegate
  440. // - This section handles files dragged and dropped onto the application icon
  441.  
  442. /* set icon button image */
  443. - _setFileIcon:imageId
  444. {
  445.     XSetAppIcon(imageId?imageId:dockIcon);
  446.     return self;
  447. }
  448.  
  449. /* files entered icon window */
  450. - (NXDragOperation)draggingEntered:sender
  451. {
  452.     Pasteboard    *pb = [sender draggingPasteboard];
  453.     pbDragFilesOk = YES;
  454.     if (![pb findAvailableTypeFrom:pbTypes num:pbNumTypes]) pbDragFilesOk = NO; else
  455.     if (pbChangeCount == [pb changeCount])                  pbDragFilesOk = NO;
  456.     if (pbDragFilesOk) { [self _setFileIcon:openFolder]; return NX_DragOperationCopy; }
  457.     return NX_DragOperationNone;
  458. }
  459.  
  460. /* file icon moved to location */
  461. - (NXDragOperation)draggingUpdated:sender
  462. {
  463.     return pbDragFilesOk? NX_DragOperationCopy : NX_DragOperationNone;
  464. }
  465.  
  466. /* mouse exited icon window */
  467. - draggingExited:sender
  468. {
  469.     [self _setFileIcon:(id)nil];
  470.     pbDragFilesOk = NO;
  471.     return self;
  472. }
  473.  
  474. /* indicate we accept dragging */
  475. - (BOOL)prepareForDragOperation:sender
  476. {
  477.     return pbDragFilesOk? YES : NO;
  478. }
  479.  
  480. /* files dropped in icon window */
  481. - (BOOL)performDragOperation:sender
  482. {
  483.     char        *fileList;
  484.     int            fileListLen;
  485.     Pasteboard    *pb = [sender draggingPasteboard];
  486.     [self _setFileIcon:(id)nil];
  487.     if (pbDragFilesOk && [pb readType:pbTypes[0] data:&fileList length:&fileListLen]) {
  488.         ParseString dragFiles = fileList;
  489.         [pb deallocatePasteboardData:fileList length:fileListLen];
  490.         pbChangeCount = [pb changeCount];
  491.         pbDragFilesOk = NO;
  492.         if (dragFiles[0]) {
  493.             [NXApp activateSelf:YES];
  494.             [[Portfolio alloc] initFromList:&dragFiles];
  495.             return YES;
  496.         }
  497.     }
  498.     return NO;
  499. }
  500.  
  501. /* clean up */
  502. - concludeDragOperation:sender
  503. {
  504.     return self;
  505. }
  506.  
  507. /* find window for number (special case for appIcon window) */
  508. - findWindow:(int)windowNum
  509. {
  510.     id    winId = [super findWindow:windowNum];
  511.     return (winId || (windowNum != appIconWinNum))? winId : [NXApp appIcon];
  512. }
  513.  
  514. // -------------------------------------------------------------------------------------
  515. // outlet initialization
  516.  
  517. - setHelpScroller:anObject
  518. {
  519.     helpScroll = [[[ScrollText newScrollText:anObject] clearScrollText] setDelegate:self];
  520.     textPrintf(helpScroll, "\n");    // this forces the scroll view to initialize
  521.     [helpScroll clearScrollText];
  522.     return self;
  523. }
  524.  
  525. @end
  526.